package red.uec.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class JwtHelper {
    private static final ThreadLocal<JwtPayload> jwtPayloadThreadLocal = new ThreadLocal<>();

    /**
     * JWT 秘钥
     *
     * TODO: 后续改为配置中心配置
     */
    private String secretKey = "e10adc3949ba59abbe56e057f20f883e";

    /**
     * JWT expiresAt相对issuedAt的时间间隔（单位：天）
     * 公式：expiresAt = issuedAt + expiresAtInterval
     *
     * TODO: 后续改为配置中心配置
     */
    private int expiresAtInterval = 10;

    /**
     * JWT生成Token.<br/>
     *
     * JWT构成: header, payload, signature
     *
     * @param jwtPayload jwt荷载
     */
    public String createToken(JwtPayload jwtPayload){
        if(null == jwtPayload){
            throw new NullPointerException("'jwtPayload' is null");
        }

        Date issuedAt = new Date();
        Date expiresAt = DateUtils.addDays(issuedAt, expiresAtInterval);

        // header Map
        Map<String, Object> header = new HashMap<>();
        header.put("alg", "HS256");
        header.put("typ", "JWT");

        // build token
        return JWT.create().withHeader(header) // header
                .withClaim("iss", "Service") // payload
                .withClaim("aud", "APP")
                .withClaim("user_id", jwtPayload.getUserId())
                .withClaim("name", jwtPayload.getName())
                .withClaim("mobile", jwtPayload.getMobile())
                .withIssuedAt(issuedAt) // sign time
                .withExpiresAt(expiresAt) // expire time
                .sign(Algorithm.HMAC256(secretKey)); // signature
    }

    /**
     * 解密Token
     *
     * @param token
     * @return
     * @throws Exception
     */
    public JwtPayload verifyToken(String token) throws JwtVerifyException,JwtExpireException {
        DecodedJWT jwt = null;
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secretKey)).build();
        try {
            jwt = verifier.verify(token);
        } catch (Exception e) {
            throw new JwtVerifyException("解密JWT发生异常", e);
        }

        String userId = jwt.getClaim("user_id").asString();
        String mobile = jwt.getClaim("mobile").asString();
        String name = jwt.getClaim("name").asString();

        if(jwt.getExpiresAt().getTime() < System.currentTimeMillis()){
            throw new JwtExpireException("登录超时异常");
        }

        return new JwtPayload(userId, mobile, name);
    }

    public static void setJwtPayload(JwtPayload jwtPayload){
        jwtPayloadThreadLocal.set(jwtPayload);
    }

    public static JwtPayload getJwtPayload(){
        return jwtPayloadThreadLocal.get();
    }

    public static String getUserId(){
        return jwtPayloadThreadLocal.get().getUserId();
    }
}
